#pragma once
#include <iostream>
#include <memory>
#include <sys/select.h>
#include "Socket.hpp"
#include "Log.hpp"

using namespace socket_ns;
using namespace log_ns;

class SelectServer {
private:
    static const int gnum = sizeof(fd_set) * 8;
    static const int gdefaultfd = -1;
    static const int gsize = 4096;

    void HandlerListenEvents()
    {
        InetAdrr Addr;
        int sockfd = _listensock->CreateAccepte(&Addr);
        if (sockfd > 0) {
            LOG(INFO, "listen socket recive a sockfd, client info: %s\n", Addr.AddrString().c_str());
            // 还不一定能将刚收到的sockfd加入到fd array中，因为array中的元素可能已经装满
            bool flag = false;
            // 将获取到的套接字放入到fd array中
            for (int j = 0; j < gnum; j++) {
                if (_fd_array[j] != gdefaultfd)
                    continue;
                _fd_array[j] = sockfd;
                flag = true;
                break;
            }
            if (!flag) {
                LOG(WARNING, "fd array is full\n");
                close(sockfd);
            }
        }

    }

    void HandlerOtherEvents(int i) {
        LOG(INFO, "other sockfd do\n");
        // 进行读操作，然后将其打印出来
        char buff[gsize];
        int n = read(_fd_array[i], buff, sizeof(buff) - 1);
        if (n > 0) {
            buff[n] = 0;
            std::cout << "echo > " << buff << std::endl;
            // 返回一个http报文
            std::string content = "<html><body><h1>hello Linux!</h1></body></html>";
            std::string echo_str = "HTTP/1.0 200 OK\r\n";
            echo_str += "Content-Type: text/html\r\n";
            echo_str += "Content-Length: " + std::to_string(content.size()) + "\r\n\r\n";
            echo_str += content;
            // echo_str += buffer;
            send(_fd_array[i], echo_str.c_str(), echo_str.size(), 0); // 临时方案
        } else if (n == 0) {
            // 读操作结束，处理结束，将套接字抹除
            LOG(INFO, "client quit...\n");
            close(_fd_array[i]);
            _fd_array[i] = gdefaultfd;        
        } else {
            LOG(ERROR, "read error\n");
            close(_fd_array[i]);
            _fd_array[i] = gdefaultfd;     
        }
    }

    void HandlerEvents(fd_set& rfds) {
        // 检查是哪些fd
        for (int i = 0; i < gnum; i++) {
            if (_fd_array[i] == gdefaultfd || !FD_ISSET(_fd_array[i], &rfds))
                continue;
            // 当走到这里的时候_fd_array[i]已经准备好了，会直接运行下去，不会阻塞
            if (_fd_array[i] == _listensock->GetSockfd()) {
                // listensocket接收到一个连接请求
                HandlerListenEvents();
            } else {
                // 其他socket收到了信息
                HandlerOtherEvents(i);
            }
        }
    }    

    void PrintDebug() {
        std::cout << "the socketfd have: ";
        for (int i = 0; i < gnum; i++) {
            if (_fd_array[i] == gdefaultfd)
                continue;
            std::cout << _fd_array[i] << " ";
        }
        std::cout << std::endl;
    }

public:
    SelectServer(uint16_t port) 
        : _listensock(std::make_unique<TcpSocket>(port))
    {
        // tcp先建立listen连接
        _listensock->BuildListenSocket(port);
    }

    void Init() {
        for (int i = 0; i < gnum; i++) {
            _fd_array[i] = gdefaultfd;
        }
        // 将listen套接字放入到数组中
        _fd_array[0] = _listensock->GetSockfd();
    }

    void Start() {
        while (true) {
            int max_fd = gdefaultfd;
            fd_set rfds;
            FD_ZERO(&rfds);     // 更新文件描述符集，每一次都需要更新

            // 只要是在文件描述符数组中，都是我们需要关注的fd，都将其加入到fd集中
            for (int i = 0; i < gnum; i++) {
                if (_fd_array[i] == gdefaultfd)
                    continue;
                FD_SET(_fd_array[i], &rfds);
                max_fd = std::max(max_fd, _fd_array[i]);
            }

            // 设置超时时间
            struct timeval timeout = {1, 0};
            int n = select(max_fd + 1, &rfds, nullptr, nullptr, &timeout);

            // sleep(1);
            if (n > 0) {
                // 现在已经发现存在文件描述符准备好
                // 可以让去直接处理对应的文件描述符
                LOG(INFO, "begin to deal with the things\n");
                // 打印当前文件描述符数组中有效的描述符
                PrintDebug();
                HandlerEvents(rfds);
            } else if (n == 0) {
                LOG(INFO, "over time: %d %d, to wait next rotate\n", timeout.tv_sec, timeout.tv_usec);
                continue;
            } else {
                LOG(ERROR, "select error\n");
                break;
            }
        }
    }

    ~SelectServer() {}
private:

    std::unique_ptr<Socket> _listensock;
    int _fd_array[gnum];
};